package cn.yh.nft.turbo.order.sharding.id;

import cn.hutool.core.util.IdUtil;
import cn.yh.nft.turbo.api.common.BusinessCode;
import cn.yh.nft.turbo.order.sharding.strategy.DefaultShardingTableStrategy;
import org.apache.commons.lang.StringUtils;

/**
 * @ClassName: DistributeID
 * @Author: 草莓爱上啵啵
 *
 * 分布式ID
 */
public class DistributeID {

    /**
     * 系统标识码
     */
    private String businessCode;

    /**
     * 表下标
     */
    private String table;

    /**
     * 序列号
     */
    private String seq;

    /**
     * 分表策略
     */
    private static DefaultShardingTableStrategy shardingTableStrategy = new DefaultShardingTableStrategy();

    public DistributeID(){}

    /**
     * 利用雪花算法生成一个唯一ID
     * @param businessCode
     * @param workerId
     * @param externalId
     * @return
     */
    public static String generateWithSnowflake(BusinessCode businessCode, long workerId, String externalId) {
        long id = IdUtil.getSnowflake(workerId).nextId();
        return generate(businessCode, externalId, id);
    }

    /**
     * 生成一个唯一ID： 10(业务码) 1769649671860822016 （sequence) 1023（分表）
     * @param businessCode
     * @param externalId
     * @param sequenceNumber
     * @return
     */
    public static String generate(BusinessCode businessCode, String externalId, Long sequenceNumber) {
        DistributeID distributeId = create(businessCode, externalId, sequenceNumber);
        return distributeId.businessCode + distributeId.seq + distributeId.table;
    }

    @Override
    public String toString(){
        return this.businessCode + this.seq + this.table;
    }

    /**
 * 创建一个分布式ID
 * 该方法根据业务代码、外部ID和序列号生成一个分布式ID对象
 * 主要用于在分布式系统中生成唯一的、有序的ID
 *
 * @param businessCode 业务代码枚举，用于区分不同的业务场景
 * @param externalId 外部ID字符串，作为生成分布式ID的一个因子
 * @param sequenceNumber 序列号，用于在相同业务代码和外部ID的情况下区分不同的ID
 * @return 返回一个填充了业务代码、表号和序列号的DistributeID对象
 */
public static DistributeID create(BusinessCode businessCode, String externalId, Long sequenceNumber) {
    // 创建一个DistributeID实例
    DistributeID distributeId = new DistributeID();

    // 将业务代码的字符串表示赋值给DistributeID的businessCode字段
    distributeId.businessCode = businessCode.getCodeString();

    // 根据外部ID和业务代码获取分表号，并将其转换为字符串
    // 这里使用了分表策略接口来确定具体的表号
    String table = String.valueOf(shardingTableStrategy.getTable(externalId, businessCode.tableCount()));

    // 将表号字符串填充至4位，不足的部分在左侧用'0'填充
    distributeId.table = StringUtils.leftPad(table, 4, "0");

    // 将序列号转换为字符串并赋值给DistributeID的seq字段
    distributeId.seq = String.valueOf(sequenceNumber);

    // 返回填充了相关信息的DistributeID对象
    return distributeId;
}


    public static String getShardingTable(DistributeID distributeId) {
        return distributeId.table;
    }

    public static String getShardingTable(String externalId, int tableCount) {
        return StringUtils.leftPad(String.valueOf(shardingTableStrategy.getTable(externalId, tableCount)), 4, "0");
    }

    public static String getShardingTable(String id) {
        return getShardingTable(valuedOf(id));
    }


    /**
 * 根据字符串ID创建DistributeID对象
 * 该方法解析输入的字符串ID，将其拆分为业务代码、序列号和表号，并赋值给DistributeID对象
 *
 * @param id 字符串形式的ID，格式为"业务代码+序列号+表号"
 * @return 返回解析后的DistributeID对象
 */
public static DistributeID valuedOf(String id) {
    DistributeID distributeId = new DistributeID();

    // 提取并赋值业务代码，假设业务代码占字符串的前2位
    distributeId.businessCode = id.substring(0,2);

    // 提取并赋值序列号，假设序列号位于业务代码之后和表号之前
    distributeId.seq = id.substring(2, id.length() - 4);

    // 提取并赋值表号，假设表号是字符串的最后4位
    distributeId.table = id.substring(id.length() - 4, id.length());

    // 返回解析后的DistributeID对象
    return distributeId;
}

}
